package com.wangshouyu.log.print;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wangshouyu.log.aop.AopRequestLogInterceptor;
import com.wangshouyu.log.enums.LogLevel;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.expression.MethodBasedEvaluationContext;
import org.springframework.core.DefaultParameterNameDiscoverer;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.support.StandardMultipartHttpServletRequest;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Arrays;

import static com.wangshouyu.log.print.Slf4jLogPrint.printLog;

/**
 * @author 王守钰
 * @date 2020年12月08日 12:30
 * @description 默认请求日志打印
 */
public class DefaultLogPrint implements LogPrint {

    private Logger log = LoggerFactory.getLogger(AopRequestLogInterceptor.class);

    private final String UNKNOWN = "unknown";

    private static final ParameterNameDiscoverer NAME_DISCOVERER = new DefaultParameterNameDiscoverer();

    private static final ExpressionParser PARSER = new SpelExpressionParser();

    private ObjectMapper objectMapper = new ObjectMapper();

    @Override
    public void request(LogLevel logLevel, Method method, Object[] arguments, String[] parameterNames) {
        try{
            // 构造request对象
            RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
            ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes;
            HttpServletRequest request = servletRequestAttributes.getRequest();
            // 构建SpEL
            EvaluationContext context = new MethodBasedEvaluationContext(null, method, arguments, NAME_DISCOVERER);
            if(null == parameterNames){
                return ;
            }
            // 循环打印参数
            for (String param : parameterNames){
                Object value = null;
                try{
                    value = PARSER.parseExpression("#" + param).getValue(context);
                }catch (Exception e){
                    log.error("spel表达式解析错误", e);
                }
                if(value instanceof MultipartFile){
                    //参数是multipart的也先去掉
                    continue;
                }
                if(value instanceof HttpServletResponse) {
                    //参数是multipart的也先去掉
                    continue;
                }
                if(value instanceof StandardMultipartHttpServletRequest){
                    //参数是multipart的也先去掉
                    continue;
                }

                if(value instanceof HttpServletRequest){
                    //参数是HttpServletRequest的也先去掉
                    continue;
                }
                printLog(log, logLevel, "{}:{}", param, objectMapper.writeValueAsString(value));
            }
            printLog(log, logLevel, "URL:{}", request.getRequestURL().toString());
            printLog(log, logLevel,"HTTP_METHOD : {}" ,request.getMethod());
            printLog(log, logLevel,"IP: {}" , getRemoteIp(request));
            printLog(log, logLevel,"CLASS_METHOD: {}", method.getDeclaringClass().getName() + "." + method.getName());
            //这个就是纯粹拿到参数，值需要自己匹配
            printLog(log, logLevel,"ARGS: {}", Arrays.toString(arguments));
        }catch (Exception e){
            log.error("记录请求日志失败");
            log.error(e.getMessage(), e);
        }

    }

    @Override
    public void result(LogLevel logLevel, Object object) {
        try {
            printLog(log, logLevel, "返回结果：{}", objectMapper.writeValueAsString(object));
        } catch (JsonProcessingException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void slow(LogLevel logLevel, String method, Long execTime) {
        printLog(log, logLevel, "{}, execTime:{}", method, execTime);
    }

    /**
     * 获取请求ip地址
     * @param request
     * @return
     */
    private String getRemoteIp(HttpServletRequest request) {
        String ip = null;

        //X-Forwarded-For：Squid 服务代理
        String ipAddresses = request.getHeader("X-Forwarded-For");
        if (ipAddresses == null || ipAddresses.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddresses)) {
            //Proxy-Client-IP：apache 服务代理
            ipAddresses = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddresses == null || ipAddresses.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddresses)) {
            //WL-Proxy-Client-IP：weblogic 服务代理
            ipAddresses = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddresses == null || ipAddresses.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddresses)) {
            //HTTP_CLIENT_IP：有些代理服务器
            ipAddresses = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ipAddresses == null || ipAddresses.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddresses)) {
            //X-Real-IP：nginx服务代理
            ipAddresses = request.getHeader("X-Real-IP");
        }

        //有些网络通过多层代理，那么获取到的ip就会有多个，一般都是通过逗号（,）分割开来，并且第一个ip为客户端的真实IP
        if (ipAddresses != null && ipAddresses.length() != 0) {
            ip = ipAddresses.split(",")[0];
        }

        //还是不能获取到，最后再通过request.getRemoteAddr();获取
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ipAddresses)) {
            ip = request.getRemoteAddr();
        }
        return ip.equals("0:0:0:0:0:0:0:1")?"127.0.0.1":ip;
    }

}
